#include "StdAfx.h"
#include "PortDevEventCallback.h"

// see:
// http://blogs.msdn.com/b/dimeby8/archive/2006/10/06/listening-to-wpd-events.aspx

PortDevEventCallback::PortDevEventCallback(void)
{
    refCount = 1;
    eventCookie = NULL;
}

PortDevEventCallback::~PortDevEventCallback(void)
{
}

HRESULT PortDevEventCallback::QueryInterface(REFIID riid, LPVOID *ppvObj)
{
    HRESULT hr = S_OK;
    if (ppvObj == NULL)
    {
        hr = E_INVALIDARG;
        return hr;
    }

    if ((riid == IID_IUnknown) ||
        (riid == IID_IPortableDeviceEventCallback))
    {
        AddRef();
        *ppvObj = this;
    }
    else
    {
        hr = E_NOINTERFACE;
    }
    
    return hr;
}

ULONG __stdcall PortDevEventCallback::AddRef()
{
    InterlockedIncrement((long*) &refCount);
    return refCount;
}

ULONG __stdcall PortDevEventCallback::Release()
{
    ULONG ulRefCount = refCount - 1;

    if (InterlockedDecrement((long*) &refCount) == 0)
    {
        delete this;
        return 0;
    }
    return ulRefCount;
}

// Register for device events. 
HRESULT __stdcall PortDevEventCallback::Register(IPortableDevice* pDevice)
{
    //
    // Check if we are already registered. If so
    // return S_FALSE
    //
    if (eventCookie != NULL)
    {
        return S_FALSE;
    }

    HRESULT hr = S_OK;

    CComPtr<IPortableDeviceValues> spEventParameters;
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) &spEventParameters);
    }

    // IPortableDevice::Advise is used to register for event notifications
    // This returns a cookie (string) that is needed while unregistering
    if (hr == S_OK)
    {
        hr = pDevice->Advise(0, this, spEventParameters, &eventCookie);
    }

    return hr;
}

// Unregister from device event notification
HRESULT __stdcall PortDevEventCallback::UnRegister(IPortableDevice* pDevice)
{
    //
    // Return S_OK if we are not registered for any thing
    //
    if (eventCookie == NULL)
    {
        return S_OK;
    }

    // IPortableDevice::Unadvise is used to stop event notification
    // We use the cookie (string) that we received while registering
    HRESULT hr = pDevice->Unadvise(eventCookie);

    // Free string allocated earlier by Advise call
    CoTaskMemFree(eventCookie);
    eventCookie = NULL;

    return hr;
}

// Main OnEvent handler called for events
HRESULT __stdcall PortDevEventCallback::OnEvent(
    IPortableDeviceValues* pEventParameters)
{
    HRESULT hr = S_OK;

    if (pEventParameters == NULL)
    {
        hr = E_POINTER;
    }

    // The pEventParameters collection contains information about the event that was
    // fired. We'll at least need the EVENT_ID to figure out which event was fired
    // and based on that retrieve additional values from the collection

    // Display the event that was fired
    GUID EventId;
    if (hr == S_OK)
    {
        hr = pEventParameters->GetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, &EventId);
    }

    LPWSTR pwszEventId = NULL;
    if (hr == S_OK)
    {
        hr = StringFromCLSID(EventId, &pwszEventId);
    }

    if (hr == S_OK)
    {
        //printf("******** Event ********\n");
        //printf("Event: %ws\n", pwszEventId);

        if (EventId == WPD_EVENT_DEVICE_REMOVED)
        {
            printf("%d Device disconnected\n", MTPconnect::M_ERROR);
            printf("%d %d\n", MTPconnect::M_STATUS, 4); // TODO: magic #
            fflush(stdout);
        }
    }

    if (pwszEventId != NULL)
    {
        CoTaskMemFree(pwszEventId);
    }

    // Display the ID of the object that was affected
    // We can also obtain WPD_OBJECT_NAME, WPD_OBJECT_PERSISTENT_UNIQUE_ID,
    // WPD_OBJECT_PARENT_ID, etc.
    /*LPWSTR pwszObjectId = NULL;
    if (hr == S_OK)
    {
        hr = pEventParameters->GetStringValue(WPD_OBJECT_ID, &pwszObjectId);
    }

    if (hr == S_OK)
    {
        printf("Object ID: %ws\n", pwszObjectId);
    }

    if (pwszObjectId != NULL)
    {
        CoTaskMemFree(pwszObjectId);
    }*/

    // Note that we intentionally do not call Release on pEventParameters since we 
    // do not own it

    return hr;
}